﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using GRMS.App_Data;

namespace GRMS.App_Code
{
    public abstract class ManagementSystem
    {
        #region Regular Expressions
        string RegexAssignString = @"^[A-Za-z0-9]* = '\w*'";
        string RegexAssignInt = @"^[A-Za-z0-9]* = [0-9]*";
        string RegexGet = @"^Get: {[A-Za-z0-9]*}";
        string RegexIdentifier = @"[A-Za-z1-9_]*";
        string RegexInt = @"^[1-9]+";
        string RegexCompareEqual = @"^[A-Za-z0-9]* == [A-Za-z0-9]*";
        string RegexCompareNotEqual = @"^[A-Za-z0-9]* != [A-Za-z0-9]*";
        string RegexCompareGreater = @"^[A-Za-z0-9]* > [A-Za-z0-9]*";
        string RegexCompareGreaterOrEqual = @"^[A-Za-z0-9]* >= [A-Za-z0-9]*";
        string RegexCompareLess = @"^[A-Za-z0-9]* < [A-Za-z0-9]*";
        string RegexCompareLessOrEqual = @"^[A-Za-z0-9]* <= [A-Za-z0-9]*";
        string RegexOption = @"^[A-Za-z0-9]* : [A-Za-z0-9]* \| [A-Za-z0-9]* \{\w*\} ";
        string RegexOptionSelect = @"^[A-Za-z0-9]* : ";
        string RegexOptionName = @"[A-Za-z0-9]+ ";
        string RegexOptionChoices = @"\| [A-Za-z0-9]* \{\w*\}";
        string RegexRollSingle = @"^Roll: [A-Za-z0-9]*d[A-Za-z0-9]+|^Roll: [A-Za-z0-9]*_[A-Za-z0-9]+";
        string RegexRollSingleMod = @"^Roll: [A-Za-z0-9]*d[A-Za-z0-9]+\+\d+|^Roll: [A-Za-z0-9]*_[A-Za-z0-9]+\+\d+";
        string RegexRollMulti = @"^Roll: [A-Za-z0-9]*d[A-Za-z0-9]+\+[A-Za-z0-9]*d[A-Za-z0-9]+|^Roll: [A-Za-z0-9]*_[A-Za-z0-9]+\+[A-Za-z0-9]*_[A-Za-z0-9]+";
        string RegexRollMultiMod = @"^Roll: [A-Za-z0-9]*d[A-Za-z0-9]+\+[A-Za-z0-9]*d[A-Za-z0-9]+\+\d+|^Roll: [A-Za-z0-9]*_[A-Za-z0-9]+\+[A-Za-z0-9]*_[A-Za-z0-9]+\+\d+";
        string RegexDieValue = @"d[A-Za-z0-9]+";
        string RegexIsDieValue = @"[A-Za-z0-9]*d[A-Za-z0-9]+|[A-Za-z0-9]*_[A-Za-z0-9]+";
        #endregion

        #region Splits and Trims
        char[] GetTrim = { '{', '}' };
        string[] CompareSplit = { "==", "!=", ">", ">=", "<", "<=" };
        char[] CompareTrim = { ' ', ':' };
        char[] RollSplit = { '_', 'd', '+' };
        char[] RollSplitMulti = { '+' };
        char[] TrimLineBreaks = { '\n', '\r' };
        #endregion

        public Session current;

        public string AcceptCommand(string s)
        {
            string[] lines = s.Split(';');
            string result = "";

            #region Test Commands
            if (lines[0].StartsWith("Test"))
            {
                switch (lines[0])
                {
                    case "Test Create J-Random":
                        Character test = new Character(current);
                        test.ID = "J-Random";
                        Dictionary<string, string> input1 = new Dictionary<string, string>();
                        foreach (KeyValuePair<string, string> str in test.GetList("attributes", null))
                            input1.Add(str.Key, (Convert.ToInt32(str.Value) + 3).ToString());
                        
                        Hashtable ht = new Hashtable();
                        ht.Add("id", "Climbing");
                        ht.Add("Climbing", "3");
                        ht.Add("section", "skills");
                        ht.Add("type", "Athletics");
                        ht.Add("attr", "Strength");
                        ht.Add("skill", "this");
                        ht.Add("combat", "false");
                        test.AddProperty(ht);
                        ht = new Hashtable();
                        ht.Add("id", "Cooking");
                        ht.Add("Cooking", "3");
                        ht.Add("section", "skills");
                        ht.Add("type", "Artistry");
                        ht.Add("attr", "Alertness");
                        ht.Add("skill", "this");
                        ht.Add("combat", "false");
                        test.AddProperty(ht);

                        test.UpdateBaseProperties(input1);

                        current.AddCharacter(test, "Test");
                        return "Successfully Created J-Random Character";
                    case "Test Change Attribute":
                        if (!current.HasCharacter("J-Random"))
                            return "J-Random not created yet.";

                        string[] split = { " " };
                        string[] attribute = lines[1].Split(split, StringSplitOptions.RemoveEmptyEntries);
                        if (!current.GetCharacter("J-Random").HasAttribute(attribute[0]))
                            return "J-Random does not have attribute " + attribute[0];

                        current.GetCharacter("J-Random").UpdateBaseProperty(new KeyValuePair<string,string>(attribute[0], attribute[1]));
                        return string.Format("Updated J-Random's skill of {0} to {1}", attribute[0], attribute[1]);
                    case "Test Action":
                        if (!current.HasCharacter("J-Random"))
                            return "J-Random not created yet.";

                        string[] action = lines[1].Trim().Split(' ');
                        if (!current.GetCharacter("J-Random").HasAttribute(action[0]))
                            return "J-Random does not have attribute " + action[0];

                        return ProcessAction(current.GetCharacter("J-Random"), action[0], lines[1].Trim().Replace(action[0], ""));
                    default:
                        return "Not a valid Test Command";
                }
            }
            #endregion
            #region Real Commands
            if (Regex.Match(s, @"Register [A-Za-z0-9]+").Success ||
                Regex.Match(s, @"Create Game Session [A-Za-z0-9]+").Success ||
                Regex.Match(s, @"Finished [A-Za-z0-9]+").Success ||
                Regex.Match(s, @"Subscribe [A-Za-z0-9]+ [0-9]+").Success)
            {
                switch (s.Split(' ')[0])
                {
                    case "Register":
                        result = Register(s.Split(' ')[1]);
                        break;
                    case "Create":
                        result = CreateSession(s.Split(' ')[3], false);
                        break;
                    case "Finished":
                        result = CreateSession(s.Split(' ')[1], true);
                        break;
                    default:
                        result = Subscribe2Session(s.Split(' ')[1], s.Split(' ')[2]);
                        break;
                }
            }
            else if (s.Split(';').Length > 1)
            {
                foreach (string str in s.Split(';'))
                {
                    if (str.Trim(TrimLineBreaks) != "")
                        result += CommonCommands.ProcessString(str.Trim(TrimLineBreaks), current);
                }            }
            else
            {
                result = CommonCommands.ProcessString(s.Trim(TrimLineBreaks), current);
                if (s == result)
                {
                    if (s.Split(' ')[s.Split(' ').Length - 1] == "GM")
                        result = ActorCommands.GMCommands(s, current);
                    else
                        result = ActorCommands.CharacterCommands(s, current, true);
                }
            }
            #endregion

            if (result == "Invalid Action")
                result = "";

            return result;

            //view(new Hashtable());  //Dependency Injection : Art.Domer@Domernet.com
        }
        private string ProcessAction(Character actor, string action, string targets)
        {
            ActionEventRequest AER = actor.GetEventRequest(action, targets);
            ActionEventFactory AEF = new ActionEventFactory(current);
            ActionEvent AE = AEF.GenerateActionEvent(AER);
            ActionEventHandler AEH = new ActionEventHandler(current);
            return AEH.ProcessActionEvent(AE);
        }

        private string Register(string ID)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            if (new ExtUserGetList(db, ID.GetHashCode().ToString()).Count == 0)
            {
                object o = db.RegisterUser(ID.GetHashCode().ToString(), ID);
                db.SubmitChanges();
                return ID + ": Registration Successful";
            }
            return "User already has a username in the database";
        }
        private string CreateSession(string ID, bool finished)
        {
            switch (finished)
            {
                case false:
                    return current.ResetGlobal(ID);
                case true:
                    return current.CloseSession(ID);
            }
            return "Invalid Command";
        }
        private string Subscribe2Session(string ID, string roomID)
        {
            return current.Subscribe(ID, roomID);
        }

        #region AsyncCallback
        public delegate void ChatFromUIEventHandler(object sender, ChatFromUIEventArgs e); //Changed
        public delegate void ChatToUIEventHandler(object sender, ChatToUIEventArgs e);     //Completed

        public event ChatFromUIEventHandler ChatFromUIProcess;
        public event ChatToUIEventHandler ChatToUIComplete;

        public class ChatFromUIEventArgs : AsyncCompletedEventArgs
        {
            private string message = "";
            private object rootID;

            public ChatFromUIEventArgs(string UIMessage, object rootId, Exception e, bool canceled, object state)
                : base(e, canceled, state)
            {
                this.message = UIMessage;
                this.rootID = rootId;
            }

            public string Message
            {
                get
                {
                    RaiseExceptionIfNecessary();

                    return message;
                }
            }

            public object RootID
            {
                get
                {
                    RaiseExceptionIfNecessary();

                    return RootID;
                }
            }
        }
        public class ChatToUIEventArgs : AsyncCompletedEventArgs
        {
            private string message = "";
            private object rootID;

            public ChatToUIEventArgs(string GRMSMessage, object rootId, Exception e, bool canceled, object state)
                : base(e, canceled, state)
            {
                this.message = GRMSMessage;
                this.rootID = rootId;
            }

            public string Message
            {
                get
                {
                    RaiseExceptionIfNecessary();

                    return message;
                }
            }

            public object RootID
            {
                get
                {
                    RaiseExceptionIfNecessary();

                    return rootID;
                }
            }
        }

        private SendOrPostCallback onChatFromUIDelegate;
        private SendOrPostCallback onChatToUIDelegate;

        protected virtual void InitializeDelegates()
        {
            onChatFromUIDelegate = new SendOrPostCallback(ChatFromUI);
            onChatToUIDelegate = new SendOrPostCallback(ChatToUI);
        }

        private delegate void WorkerEventhandler(string message, AsyncOperation asyncOp);
        private HybridDictionary userStateToLifetime = new HybridDictionary();

        private void ChatToUI(object operationState)
        {
            ChatToUIEventArgs e = operationState as ChatToUIEventArgs;

            OnChatToUI(e);
        }
        private void ChatFromUI(object state)
        {
            ChatFromUIEventArgs e = state as ChatFromUIEventArgs;

            OnChatFromUI(e);
        }

        protected void OnChatToUI(ChatToUIEventArgs e)
        {
            if (ChatToUIComplete != null)
            {
                ChatToUIComplete(this, e);
            }
        }
        protected void OnChatFromUI(ChatFromUIEventArgs e)
        {
            if (ChatFromUIProcess != null)
            {
                ChatFromUIProcess(this, e);
            }
        }

        private void CompletionMethod(string message, Exception exception, bool canceled, AsyncOperation asyncOp)
        {
            if (!canceled)
            {
                lock (userStateToLifetime.SyncRoot)
                {
                    userStateToLifetime.Remove(asyncOp.UserSuppliedState);
                }
            }

            ChatToUIEventArgs e = new ChatToUIEventArgs(message, current.RootID, exception, canceled, asyncOp.UserSuppliedState);

            asyncOp.PostOperationCompleted(onChatToUIDelegate, e);
        }

        private bool TaskCanceled(object taskID)
        {
            return (userStateToLifetime[taskID] == null);
        }

        private void ProcessMessageWorker(string UImessage, AsyncOperation asyncOp)
        {
            string result = "";
            Exception e = null;

            if (!TaskCanceled(asyncOp.UserSuppliedState))
            {
                try
                {
                    //ToDo: Real interaction work goes here.
                    //ToDo: Add TaskCanceled check to functional process if necessary, as per website
                    result = AcceptCommand(UImessage);

                    if (result == UImessage)
                        result = "";
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }

            this.CompletionMethod(result, e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
        }

        public virtual void ChatToGRMSAsync(string UIMessage, object taskID)
        {
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskID);

            lock (userStateToLifetime.SyncRoot)
            {
                if (userStateToLifetime.Contains(taskID))
                {
                    throw new ArgumentException(
                        "Task ID parameter must be unique",
                        "taskID");
                }

                userStateToLifetime[taskID] = asyncOp;
            }

            WorkerEventhandler workerDelagate = new WorkerEventhandler(ProcessMessageWorker);
            workerDelagate.BeginInvoke(
                UIMessage,
                asyncOp,
                null,
                null);
        }

        public void CancelAsync(object taskId)
        {
            AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
            if (asyncOp != null)
            {
                lock (userStateToLifetime.SyncRoot)
                {
                    userStateToLifetime.Remove(taskId);
                }
            }
        }
        #endregion
    }
}
